home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Container Common / xbinding.cpp < prev    next >
Text File  |  1997-01-03  |  13KB  |  519 lines

  1. //
  2. //    XBINDING.CPP
  3. //
  4. //    Copyright (C) Microsoft Corporation, 1996
  5. //
  6. //    Implements the asynchronous binding model on top of the Netscape plugin
  7. //    APIs.
  8. //
  9.  
  10. #include "headers.h"
  11.  
  12. //    Default FORMATETC that is passed to IBindStatusCallback::OnDataAvailable.
  13. FORMATETC g_NullFormatEtc = {
  14.     CF_NULL,
  15.     NULL,
  16.     DVASPECT_CONTENT,
  17.     -1,
  18.     TYMED_NULL
  19. };
  20.  
  21. OLECHAR g_szBSCBHolder[] = "_BSCB_Holder_";
  22.  
  23. CXBinding::~CXBinding()
  24. {
  25.     if (m_pbsc != NULL)
  26.         m_pbsc->Release();
  27. }
  28.  
  29. //
  30. //    CXBinding::Bind
  31. //
  32.  
  33. HRESULT
  34. CXBinding::Bind(LPOLESTR pszURL, LPBINDSTATUSCALLBACK pbsc, unsigned long BindType)
  35. {
  36.     HRESULT hr;
  37.     DWORD bindf;
  38.     BINDINFO bindinfo;
  39.     CNetscapeStreamNotify *pBindingNotify;
  40.  
  41.     //    Query the client for the type of binding operation they want.
  42.     memset(&bindinfo, 0, sizeof(bindinfo));
  43.     bindinfo.cbSize = sizeof(bindinfo);
  44.     pbsc->GetBindInfo(&bindf, &bindinfo);
  45.  
  46.     //    BUGBUG:  What exactly are we supposed to do with this?
  47.     if (bindinfo.szExtraInfo != NULL)
  48.         CoTaskMemFree(bindinfo.szExtraInfo);
  49.  
  50.     //    Verify that the client is asking for an async, non-blocking, push-data
  51.     //    read binding.
  52.     if (!(bindf & BINDF_ASYNCHRONOUS) || !(bindf & BINDF_ASYNCSTORAGE) ||
  53.             ((bindf & BINDF_PULLDATA) && (BindType == TYMED_FILE || BindType == TYMED_FSP))
  54.             || (bindinfo.dwBindVerb != BINDVERB_GET))
  55.         return ResultFromScode(E_FAIL);
  56.  
  57.     //    Notify the callback that we're about to start binding.
  58.     hr = pbsc->OnStartBinding(BSCO_ALLONIBSC, (LPBINDING) this);
  59.     if (m_fAborted)
  60.         hr = ResultFromScode(E_ABORT);
  61.  
  62.  
  63.     if (SUCCEEDED(hr))
  64.     {
  65.         //    Create the appopriate stream notification object.
  66.         if (BindType == TYMED_FILE || BindType == TYMED_FSP)
  67.         {
  68.             pBindingNotify = new CBindingFileStreamNotify(this, BindType);
  69.         }
  70.         else if(bindf & BINDF_PULLDATA)
  71.         {
  72.             pBindingNotify = new CBindingPullStreamNotify(this);
  73.         }
  74.         else
  75.         {
  76.             pBindingNotify = new CBindingMemoryStreamNotify(this);
  77.         }
  78.  
  79.         if ((pBindingNotify == NULL) ||
  80.             ((pBindingNotify->m_pszURL = OleStrdup(pszURL)) == NULL))
  81.         {
  82.             hr = ResultFromScode(E_OUTOFMEMORY);
  83.         }
  84. #ifdef PLUGIN_ADAPTER
  85.         else if (NPN_GetURL((NPP) m_pSite->m_ClientInstance,
  86.             pBindingNotify->m_pszURL, NULL) != NPERR_NO_ERROR)
  87. #elif defined(URLMON_DLL)
  88.         else if (AXGetUrl( pBindingNotify->m_pszURL, pBindingNotify,
  89.                     BindType == TYMED_FILE || BindType == TYMED_FSP) != NPERR_NO_ERROR) 
  90. #else 
  91.         else if (AXE_GetURL((NPP) m_pSite->m_ClientInstance,
  92.             pBindingNotify->m_pszURL, NULL, pBindingNotify) != NPERR_NO_ERROR) 
  93. #endif
  94.         {
  95.             delete pBindingNotify;
  96.             pBindingNotify = NULL;
  97.             hr = ResultFromScode(E_FAIL);
  98.         } else {
  99. #ifdef PLUGIN_ADAPTER
  100.             //    Link the notification object to the list of objects waiting to
  101.             //    be bound.
  102.             pBindingNotify->m_pNextUnattached = m_pSite->m_pUnattachedList;
  103.             m_pSite->m_pUnattachedList = pBindingNotify;
  104. #endif
  105.  
  106.             m_pbsc = pbsc;
  107.             m_pbsc->AddRef();
  108.  
  109.             //    Result indicates that we're in the process of binding, not an error.
  110.             hr = ResultFromScode(E_PENDING);
  111.         }
  112.     }
  113.  
  114.     //    If for any reason we're going to return E_PENDING, we need to call
  115.     //    OnStopBinding and notify it of the error.
  116.     if (hr != ResultFromScode(E_PENDING)) {
  117.         pbsc->OnStopBinding(hr, NULL);
  118.     }
  119.  
  120.     return hr;
  121. }
  122.  
  123. //
  124. //    CXBinding::IUnknown::QueryInterface
  125. //
  126. //    Returns a pointer to the specified interface on a component to which a
  127. //    client currently holds an interface pointer.
  128. //
  129.  
  130. STDMETHODIMP
  131. CXBinding::QueryInterface(REFIID riid, LPVOID *ppvObj)
  132. {
  133.     HRESULT hr;
  134.     LPVOID pv;
  135.  
  136.     if (riid == IID_IUnknown || riid == IID_IBinding) {
  137.         pv = (LPVOID)(LPBINDCTX) this;
  138.         ++m_cRef;
  139.         hr = ResultFromScode(S_OK);
  140.     } else {
  141.         pv = NULL;
  142.         hr = ResultFromScode(E_NOINTERFACE);
  143.     }
  144.  
  145.     *ppvObj = pv;
  146.     return hr;
  147. }
  148.  
  149. //
  150. //    CXBinding::IUnknown::AddRef
  151. //
  152. //    Increments the reference count for the calling interface.
  153. //
  154.  
  155. STDMETHODIMP_(ULONG)
  156. CXBinding::AddRef(void)
  157. {
  158.     return ++m_cRef;
  159. }
  160.  
  161. //
  162. //    CXBinding::IUnknown::Release
  163. //
  164. //    Decrements the reference count for the calling interface on a object.  If
  165. //    the reference count on the object falls to zero, the object is freed.
  166. //
  167.  
  168. STDMETHODIMP_(ULONG)
  169. CXBinding::Release(void)
  170. {
  171.     if (--m_cRef != 0)
  172.         return m_cRef;
  173.  
  174.     delete this;
  175.     return 0;
  176. }
  177.  
  178. //
  179. //    CXBinding::IBinding::Abort
  180. //
  181. //    Permanently aborts the bind operation.
  182. //
  183.  
  184. STDMETHODIMP
  185. CXBinding::Abort(void)
  186. {
  187.     HRESULT hr;
  188.  
  189.     //    The binding operation will be aborted inside the Xxx::OnWrite method.
  190.     //    As documented in the "Asynchronous Moniker" spec, the client may
  191.     //    continue to receive callback notifications, so we can do this at our
  192.     //    earliest convenience.
  193.     if (!m_fAborted) {
  194.         m_fAborted = TRUE;
  195.         hr = ResultFromScode(S_OK);
  196.     } else {
  197.         hr = ResultFromScode(S_FALSE);
  198.     }
  199.  
  200.     return hr;
  201. }
  202.  
  203. //
  204. //    CXBinding::IBinding::Suspend
  205. //
  206. //    Suspends the bind operation until resumed by a call to IBinding::Resume or
  207. //    aborted by a call to IBinding::Abort.
  208. //
  209.  
  210. STDMETHODIMP
  211. CXBinding::Suspend(void)
  212. {
  213.     //    Suspend/resume not supported.
  214.     return ResultFromScode(E_NOTIMPL);
  215. }
  216.  
  217. //
  218. //    CXBinding::IBinding::Resume
  219. //
  220. //    Resumes a binding operation that was suspended by a call to
  221. //    IBinding::Suspend.
  222. //
  223.  
  224. STDMETHODIMP
  225. CXBinding::Resume(void)
  226. {
  227.     //    Suspend/resume not supported.
  228.     return ResultFromScode(E_NOTIMPL);
  229. }
  230.  
  231. //
  232. //    CXBinding::IBinding::SetPriority
  233. //
  234. //    Establishes the priority for the bind operation.
  235. //
  236.  
  237. STDMETHODIMP
  238. CXBinding::SetPriority(LONG nPriority)
  239. {
  240. #pragma unused (nPriority)
  241.     //    Priority cannot be controlled.
  242.     return ResultFromScode(E_NOTIMPL);
  243. }
  244.  
  245. //
  246. //    CXBinding::IBinding::GetPriority
  247. //
  248. //    Retrieves the current priority of this bind operation.
  249. //
  250.  
  251. STDMETHODIMP
  252. CXBinding::GetPriority(LONG *pnPriority)
  253. {
  254.     //    Priority cannot be controlled.
  255.     *pnPriority = 0;                    //    THREAD_PRIORITY_NORMAL.
  256.     return ResultFromScode(E_NOTIMPL);
  257. }
  258.  
  259. //
  260. //    CXBinding::IBinding::GetBindResult
  261. //
  262. //    Queries the protocol-specific outcome of a bind operation, typically used
  263. //    during IBindStatusCallback::OnStopBinding.
  264. //
  265.  
  266. STDMETHODIMP
  267. CXBinding::GetBindResult(LPCLSID pclsidProtocol, LPDWORD pdwResult, LPOLESTR
  268.     *pszResult, LPDWORD pdwReserved)
  269. {
  270. #pragma unused (pclsidProtocol, pdwReserved)
  271.     //    We don't have any protocol-specific code, so there's nothing useful to
  272.     //    return.
  273.     *pdwResult = 0;
  274.     *pszResult = NULL;
  275.     return ResultFromScode(S_OK);
  276. }
  277.  
  278. //
  279. //    CBindingMemoryStreamNotify::OnWrite
  280. //
  281. //    Extends the default memory stream implementation by notifying the client of
  282. //    data availability.
  283. //
  284.  
  285. int32
  286. CBindingMemoryStreamNotify::OnWrite(NPStream *stream, int32 offset, int32 len,
  287.     void *buffer)
  288. {
  289.     int32 BytesWritten;
  290.     ULONG BindStatus;
  291.     DWORD fBSCF;
  292.  
  293.     //    Tag the stream such that requests for unavailable data made against our
  294.     //    IStream return E_PENDING.
  295.     m_fAsyncStreamInProgress = TRUE;
  296.  
  297.     BytesWritten = CMemoryOleStreamNotify::OnWrite(stream, offset, len, buffer);
  298.  
  299.     if (BytesWritten > 0) {
  300.  
  301.         if (!m_fDeliveredFirstNotify) {
  302.             m_fDeliveredFirstNotify = TRUE;
  303.             BindStatus = BINDSTATUS_BEGINDOWNLOADDATA;
  304.             fBSCF = BSCF_FIRSTDATANOTIFICATION;
  305.         } else {
  306.             BindStatus = BINDSTATUS_DOWNLOADINGDATA;
  307.             fBSCF = BSCF_INTERMEDIATEDATANOTIFICATION;
  308.         }
  309.  
  310.         m_pBinding->m_pbsc->OnProgress((ULONG) m_StreamLength, 
  311.             (ULONG) m_StreamLength, BindStatus, m_pszURL);
  312.  
  313.         mStorageMedium.tymed = TYMED_ISTREAM;
  314.         mStorageMedium.pstm = (LPSTREAM) this;
  315.         mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
  316.         AddRef();        // account for mStorageMedium reference
  317.         m_pBinding->m_pbsc->OnDataAvailable(fBSCF, BytesWritten,
  318.             &g_NullFormatEtc, &mStorageMedium);
  319.  
  320.         //    If the client called IBinding::Abort, return an error so as to
  321.         //    force the stream to terminate.
  322.         if (m_pBinding->m_fAborted)
  323.             BytesWritten = -1;
  324.     }
  325.  
  326.     //    If BytesWritten is negative, then the stream will be terminated.  The
  327.     //    client will be notified of the error in the OnDestroyStream method.
  328.     return BytesWritten;
  329. }
  330.  
  331. //
  332. //    CBindingMemoryStreamNotify::OnDestroyStream
  333. //
  334. //    Extends the default memory stream implementation by notifying the client
  335. //    that the binding operation is complete.
  336. //
  337.  
  338. NPError
  339. CBindingMemoryStreamNotify::OnDestroyStream(NPStream *stream, NPError reason)
  340. {
  341. #pragma unused (stream)
  342.     HRESULT hr;
  343.  
  344.     m_fAsyncStreamInProgress = FALSE;
  345.  
  346.     //    You would think Netscape would pass the right reason code to us, but it
  347.     //    always returns a zero on success, so maybe they want NPERR_NO_ERROR???
  348.     if ((reason == NPERR_NO_ERROR || reason == NPRES_DONE) &&
  349.         !m_pBinding->m_fAborted) {
  350.         //    Send the final progress notification.
  351.         m_pBinding->m_pbsc->OnProgress((ULONG) m_StreamLength,
  352.             (ULONG) m_StreamLength, BINDSTATUS_ENDDOWNLOADDATA, m_pszURL);
  353.  
  354.         //    Although the data stream hasn't changed, we need to send the last
  355.         //    data notification.
  356.         mStorageMedium.tymed = TYMED_ISTREAM;
  357.         mStorageMedium.pstm = (LPSTREAM) this;
  358.         mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
  359.         m_pBinding->m_pbsc->OnDataAvailable(BSCF_LASTDATANOTIFICATION,
  360.             m_StreamLength, &g_NullFormatEtc, &mStorageMedium);
  361.  
  362.         hr = ResultFromScode(S_OK);
  363.     } else {
  364.         hr = ResultFromScode(E_ABORT);
  365.     }
  366.  
  367.     //    Notify the callback that the binding has stopped.  Plus, we're through
  368.     //    with the binding object, so we're ready to throw it away.  The client
  369.     //    is supposed to release the pointer in the OnStopBinding method, so we'll
  370.     //    make sure we don't leak anything by just deleting the binding.
  371.     m_pBinding->m_pbsc->OnStopBinding(hr, NULL);
  372.     delete m_pBinding;
  373.     m_pBinding = NULL;
  374.  
  375.     //    We're through with this binding callback object.  The user may be
  376.     //    holding on to our stream object though, so we must pay attention to our
  377.     //    reference count.
  378.     this->Release();
  379.  
  380.     return NPERR_NO_ERROR;
  381. }
  382.  
  383. //
  384. //    CBindingFileStreamNotify::IUnknown::QueryInterface
  385. //
  386.  
  387. STDMETHODIMP
  388. CBindingFileStreamNotify::QueryInterface(REFIID riid, LPVOID *ppvObj)
  389. {
  390.     HRESULT hr;
  391.     LPVOID pv;
  392.  
  393.     if (riid == IID_IUnknown) {
  394.         pv = (LPVOID)(LPUNKNOWN) this;
  395.         ++m_cRef;
  396.         hr = ResultFromScode(S_OK);
  397.     } else {
  398.         pv = NULL;
  399.         hr = ResultFromScode(E_NOINTERFACE);
  400.     }
  401.  
  402.     *ppvObj = pv;
  403.     return hr;
  404. }
  405.  
  406. //
  407. //    CBindingFileStreamNotify::IUnknown::AddRef
  408. //
  409.  
  410. STDMETHODIMP_(ULONG)
  411. CBindingFileStreamNotify::AddRef(void)
  412. {
  413.     return ++m_cRef;
  414. }
  415.  
  416. //
  417. //    CBindingFileStreamNotify::IUnknown::Release
  418. //
  419.  
  420. STDMETHODIMP_(ULONG)
  421. CBindingFileStreamNotify::Release(void)
  422. {
  423.     if (--m_cRef != 0)
  424.         return m_cRef;
  425.  
  426.     delete this;
  427.     return 0;
  428. }
  429.  
  430. //
  431. //    CBindingFileStreamNotify::OnNewStream
  432. //
  433.  
  434. NPError
  435. CBindingFileStreamNotify::OnNewStream(NPMIMEType type, NPStream *stream, NPBool
  436.     seekable, uint16 *stype)
  437. {
  438. #pragma unused (type, stream, seekable)
  439.     //    REVIEW:  We're not sending any notifications to the client, but there's
  440.     //    not much useful information that we can provide.  The client initiated
  441.     //    the download, so they probably don't want to abort at the very start of
  442.     //    the process.  The next notification we get back from the browser is the
  443.     //    filename of the data, so we there's nothing to abort.
  444.     *stype = NP_ASFILE;
  445.     return NPERR_NO_ERROR;
  446. }
  447.  
  448. //
  449. //    CBindingFileStreamNotify::OnStreamAsFile
  450. //
  451.  
  452. void
  453. CBindingFileStreamNotify::OnStreamAsFile(NPStream *stream, const char* fname)
  454. {
  455. #pragma unused (stream)
  456.     HRESULT hr = S_OK;
  457.  
  458.     if (fname != NULL)
  459.     {
  460.         //    Note that we pass ourselves as the IUnknown that will release this
  461.         //    resource.  Default behavior for the TYMED_FILENAME type is to
  462.         //    delete the file, but we want to keep it around in the cache.  Plus,
  463.         //    we may want to do something more in the future.  The filename is
  464.         //    always released by the client (see ReleaseStgMedium).
  465.         mStorageMedium.tymed = mBindType;
  466.         mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
  467.         if(mBindType == TYMED_FILE)
  468.         {
  469.             mStorageMedium.lpszFileName = OleStrdup(fname);
  470.             if(mStorageMedium.lpszFileName == NULL)
  471.                 hr = E_OUTOFMEMORY;
  472.         }
  473.         else if(mBindType == TYMED_FSP)
  474.         {
  475.             mStorageMedium.pFSSpec = (FSSpec*)CoTaskMemAlloc(sizeof(FSSpec));
  476.             if(mStorageMedium.pFSSpec == NULL)
  477.                 hr = E_OUTOFMEMORY;
  478.             else
  479.             {
  480.                 Str255 FileName;
  481.                 
  482.                 FileName[0] = strlen(fname);
  483.                 strncpy((char*)&FileName[1], fname, FileName[0]);
  484.                 FSMakeFSSpec(0, 0, FileName, mStorageMedium.pFSSpec);
  485.             }
  486.         }
  487.         if (hr == S_OK)
  488.         {
  489.             ++m_cRef;                   //    Account for 'mStorageMedium' reference
  490.             m_pBinding->m_pbsc->OnDataAvailable(BSCF_LASTDATANOTIFICATION, 0,
  491.                 &g_NullFormatEtc, &mStorageMedium);
  492.         }
  493.     } else {
  494.         hr = E_FAIL;
  495.     }
  496.  
  497.     //    Notify the callback that the binding has stopped.  Plus, we're through
  498.     //    with the binding object, so we're ready to throw it away.  The client
  499.     //    is supposed to release the pointer in the OnStopBinding method, so we'll
  500.     //    make sure we don't leak anything by just deleting the binding.
  501.     m_pBinding->m_pbsc->OnStopBinding(hr, NULL);
  502.     delete m_pBinding;
  503.     m_pBinding = NULL;
  504. }
  505.  
  506. //
  507. //    CBindingFileStreamNotify::OnDestroyStream
  508. //
  509.  
  510. NPError
  511. CBindingFileStreamNotify::OnDestroyStream(NPStream *stream, NPError reason)
  512. {
  513. #pragma unused (stream, reason)
  514.     //    The client may still have a reference to our object from the STGMEDIUM
  515.     //    structure.
  516.     this->Release();
  517.     return NPERR_NO_ERROR;
  518. }
  519.